#include "Renderer.h"
#include <GL/glfw.h>
#include "Window.h"

using namespace std;

namespace core
{
	Color Renderer::_bgColor = Color::BLACK;
	TextureStore Renderer::_textureStore;

	Color Renderer::GetColor( )
	{
		return _bgColor;
	}

	TextureStore Renderer::GetTextureStore()
	{
		return _textureStore;
	}

	void Renderer::SetColor( const Color& color )
	{
		_bgColor = color;
	}

	void Renderer::WindowResized( Size newSize )
	{
		glViewport( 0, 0, newSize.Width, newSize.Height );
	}

	void Renderer::Create( const Color& bgColor )
	{
		_bgColor = bgColor;
		glEnable(GL_COLOR_MATERIAL);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable( GL_DEPTH_TEST );
		glEnable( GL_ALPHA_TEST );
		glEnable(GL_TEXTURE_2D);

		//glEnable(GL_FOG);					// �������� ����� (GL_FOG)
		//glFogi(GL_FOG_MODE, GL_LINEAR );	// �������� ��� ������
		//float fogColor[4] = { _bgColor.R, _bgColor.G, _bgColor.B, _bgColor.A };
		//glFogfv(GL_FOG_COLOR, fogColor );	// ������������� ���� ������
		//glFogf(GL_FOG_DENSITY, 0.25f );		// ��������� ������ ����� �����
		//glHint(GL_FOG_HINT, GL_NICEST );	// ��������������� ��������� ������
		//glFogf(GL_FOG_START, 1.0f);			// �������, � ������� ���������� �����
		//glFogf(GL_FOG_END, 30.0f);			// �������, ��� ����� �������������.

		//	glEnable (GL_LIGHTING);

		glClearColor( _bgColor.R, _bgColor.G, _bgColor.B, _bgColor.A );
		glClearDepth(1.0f);									// Depth Buffer Setup

		TextureIdPool::Create(128);
	}

	void Renderer::Destroy( )
	{
		TextureIdPool::Destroy();
	}

	void Renderer::SetCamera( const Transform& tf, float scale )
	{
		glMatrixMode( GL_PROJECTION );
		glLoadIdentity( );
		Size size = Window::GetSize( );
		float aspect = float( size.Width ) / float( size.Height );
		glOrtho( -aspect, aspect, -1.0f, 1.0f, 1.0f, -1.0f );

		glMatrixMode( GL_MODELVIEW );
		glLoadIdentity( );
		glScalef( scale, scale, scale );
		glRotatef( -ToDegrees( tf.Rot ), 0.0, 0.0, 1.0 );
		glTranslatef( -tf.Vec.X, -tf.Vec.Y, 0.0f );
		//float* array = tf.GetMatrix().ToArray();
		//glMultMatrixf( array );
	}

	void Renderer::BeginDraw( )
	{
		glEnable(GL_COLOR_MATERIAL);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable( GL_DEPTH_TEST );
		glEnable( GL_ALPHA_TEST );
		glEnable(GL_TEXTURE_2D);

		glClearColor( _bgColor.R, _bgColor.G, _bgColor.B, _bgColor.A );
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

		//for( uint i = 0; i < LightDef::Count; ++i ) {
		//	LightDef* def = LightDef::lights[i];

		//	glLightfv( GL_LIGHT0 + def->Index, GL_AMBIENT, def->Ambient.Array() );
		//	glLightfv( GL_LIGHT0 + def->Index, GL_DIFFUSE, def->Diffuse.Array() );
		//	glLightfv( GL_LIGHT0 + def->Index, GL_SPECULAR, def->Specular.Array() );
		//	glEnable( GL_LIGHT0 + def->Index );

		//	Vec4 lPos = Vec4( def->Pos.Vec, 1.0f );
		//	glLightfv (GL_LIGHT0 + i, GL_POSITION, lPos.Array() );
		//}
	}

	void Renderer::EndDraw( )
	{
		glfwSwapBuffers( );
	}

	void Renderer::Push( )
	{
		glPushMatrix( );
	}

	void Renderer::Pop( )
	{
		glPopMatrix( );
	}

	void Renderer::MoveTo( const Transform& transform )
	{
		float* array = transform.GetMatrix( ).ToArray();
		glLoadMatrixf( array );
	}

	void Renderer::MoveBy( const Transform& transform )
	{
		glTranslatef( transform.Vec.X, transform.Vec.Y, 0.0f );
		glRotatef( ToDegrees( transform.Rot ), 0.0f, 0.0f, 1.0f );
	}

	void Renderer::DrawTransform( const Transform& transform, const Color& color, float size )
	{
		glPushMatrix();

		glTranslatef(transform.Vec.X, transform.Vec.Y, 0.0f);
		glRotatef(ToDegrees( -transform.Rot ), 0.0f, 0.0f, 1.0f);

        Vec3 center(Vec2::ZERO.X, Vec2::ZERO.Y, -0.9 );
        Vec3 up(Vec2::UP.X * size, Vec2::UP.Y * size, -0.9 );
        Vec3 right(Vec2::RIGHT.X * size, Vec2::RIGHT.Y * size, -0.9 );
        DrawLine(center, center + up, color);
        DrawLine(center, center + right, color);

        glPopMatrix();
	}


	void Renderer::DrawPoint( const Vec3& point, const Color& color, float size )
	{
		glEnable( GL_ALPHA_TEST );
		glEnable( GL_BLEND );
        glDisable(GL_DEPTH_TEST);

		glColor4f( color.R, color.G, color.B, color.A );
		float s = 0.5f * size;
		glBegin( GL_LINES );
			glVertex3f( point.X - s, point.Y, point.Z );
			glVertex3f( point.X + s, point.Y, point.Z );
			glVertex3f( point.X, point.Y - s, point.Z );
			glVertex3f( point.X, point.Y + s, point.Z );
			glVertex3f( point.X, point.Y, point.Z - s );
			glVertex3f( point.X, point.Y, point.Z + s );
		glEnd( );

		glDisable( GL_BLEND );
		glDisable( GL_ALPHA_TEST );
	}

	void Renderer::DrawLine( const Vec3& start, const Vec3& end, const Color& color )
	{
		glEnable( GL_ALPHA_TEST );
		glEnable( GL_BLEND );
        glEnable(GL_DEPTH_TEST);

		glColor4fv( color.Array() );
		//glColor3f( color.R, color.G, color.B, color.A );
		glPointSize( 1.0f );
		glLineWidth(1.0f);

		glBegin( GL_LINES );
			glVertex3f( start.X, start.Y, start.Z );
			glVertex3f( end.X, end.Y, end.Z );
		glEnd( );

		//glDisable( GL_BLEND );
		//glDisable( GL_ALPHA_TEST );
	}

	void Renderer::DrawPolygon( const IIterable<Vec3>* vertices, const Color& color )
	{
		glEnable( GL_ALPHA_TEST );
		glEnable( GL_BLEND );
        glDisable(GL_DEPTH_TEST);

		glColor4fv( color.Array( ) );

		glBegin( GL_LINE_LOOP );
		for(
			IteratorPtr<Vec3> iter( vertices->GetIterator() );
			!iter->IsEnd();
			iter->Next()
		)
		{
			Vec3 vertex = iter->Current();
			glVertex3f( vertex.X, vertex.Y, vertex.Z );
		}
		glEnd( );

		glDisable( GL_BLEND );
		glDisable( GL_ALPHA_TEST );
	}

	void Renderer::DrawFilledPolygon( const IIterable<Vec3>* vertices, const Color& borderColor, const Color& fillColor )
	{
		glEnable( GL_ALPHA_TEST );
		glEnable( GL_BLEND );
        glDisable(GL_DEPTH_TEST);

		glColor4fv( borderColor.Array( ) );

		glBegin( GL_LINE_LOOP );
		for(
			IteratorPtr<Vec3> iter( vertices->GetIterator() );
			!iter->IsEnd();
			iter->Next()
		)
		{
			Vec3 vertex = iter->Current();
			glVertex3f( vertex.X, vertex.Y, vertex.Z );
		}
		glEnd( );

		glColor4fv( fillColor.Array( ) );

		glBegin( GL_TRIANGLE_FAN );
		for(
			IteratorPtr<Vec3> iter( vertices->GetIterator() );
			!iter->IsEnd();
			iter->Next()
		)
		{
			Vec3 vertex = iter->Current();
			glVertex3f( vertex.X, vertex.Y, vertex.Z );
		}
		glEnd( );

		glDisable( GL_BLEND );
		glDisable( GL_ALPHA_TEST );
	}

	void Renderer::DrawCircle( const Vec3& center, float radius, const Color& color ) {
		const float segments = 16.0f;
		const float increment = 2.0f * PI / segments;
		float theta = 0.0f;

		glEnable( GL_ALPHA_TEST );
		glEnable( GL_BLEND );
        glDisable(GL_DEPTH_TEST);

		glColor4fv( color.Array( ) );
		glBegin( GL_LINE_LOOP );
		for( int i = 0; i < segments; ++i ) {
			Vec3 v = center + radius * Vec3( cosf( theta ), sinf( theta ), 0.0f );
			glVertex2f( v.X, v.Y );
			theta += increment;
		}
		glEnd( );

		glDisable( GL_BLEND );
		glDisable( GL_ALPHA_TEST );
	}

	void Renderer::DrawFilledCircle( const Vec3& center, float radius, const Color& borderColor, const Color& fillColor ) {
		const float segments = 16.0f;
		const float increment = 2.0f * PI / segments;
		float theta = 0.0f;

		glEnable( GL_ALPHA_TEST );
		glEnable( GL_BLEND );
        glDisable(GL_DEPTH_TEST);

		glColor4fv( borderColor.Array( ) );
		glBegin( GL_LINE_LOOP );
		for( int i = 0; i < segments; ++i ) {
			Vec3 v = center + radius * Vec3( cosf( theta ), sinf( theta ), 0.0f );
			glVertex2f( v.X, v.Y );
			theta += increment;
		}
		glEnd( );

		glColor4fv( fillColor.Array( ) );
		glBegin( GL_TRIANGLE_FAN );
		for( int i = 0; i < segments; ++i ) {
			Vec3 v = center + radius * Vec3( cosf( theta ), sinf( theta ), 0.0f );
			glVertex2f( v.X, v.Y );
			theta += increment;
		}
		glEnd( );

		glDisable( GL_BLEND );
		glDisable( GL_ALPHA_TEST );
	}


	void Renderer::DrawTexture( const Texture* texture, const Transform& transform, float width, float height )
	{
		Vec3 center(transform.Vec.X, transform.Vec.Y, 0.0f);
		DrawTexture(texture, center, transform.Rot, width, height);
	}

	void Renderer::DrawTexture( const Texture* texture, const Vec3& center, float width, float height )
	{
		DrawTexture(texture, center, 0.0f, width, height);
	}

	void Renderer::DrawTexture( const Texture* texture, const Vec3& center, float angle, float width, float height )
	{
		glPushMatrix();

		glTranslatef(center.X, center.Y, 0.0f);
		glRotatef(ToDegrees( -angle ), 0.0f, 0.0f, 1.0f);

		float right = -width / 2;
		float left = width / 2;
		float top = height / 2;
		float bottom = -height / 2;


		glEnable(GL_ALPHA_TEST);
        glEnable(GL_BLEND);     // Turn Blending On
        glDisable(GL_DEPTH_TEST);   // Turn Depth Testing Off
		glEnable(GL_TEXTURE_2D);

		glColor4f( 1.0f, 1.0f, 1.0f, 1.0f);

		glBindTexture(GL_TEXTURE_2D, texture->GetGlId());
		glBegin( GL_QUADS);
			glTexCoord2f(0.0f, 0.0f); glVertex3f(left, bottom,  0.1f);
			glTexCoord2f(1.0f, 0.0f); glVertex3f(right, bottom,  0.1f);
			glTexCoord2f(1.0f, 1.0f); glVertex3f(right,  top,  0.1f);
			glTexCoord2f(0.0f, 1.0f); glVertex3f(left,  top,  0.1f);
		glEnd();
		glBindTexture(GL_TEXTURE_2D, 0);

		glPopMatrix();

		glDisable(GL_TEXTURE_2D);
        glEnable(GL_DEPTH_TEST);
        glDisable(GL_BLEND);
		glDisable(GL_ALPHA_TEST);
	}
}
